package bll

import (
	"context"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"go-micro.dev/v4/client"
	"manager/lib/trace_log"
	"time"
	"unsafe"

	"github.com/gin-gonic/gin"
	"github.com/prometheus/common/log"
	"manager/client/grpc"
	"manager/errors"
	"manager/event"
	"manager/model"
	"manager/model/po"
	SignalPo "manager/model/signal"
	"manager/store/redis"
	"manager/utils"
	"manager/utils/goroutine_pool"
	"proto/dto"
)

var SignalV2 = &signalV2{}

// Signal 信号模块
type signalV2 struct {
}

// SendCmdToDevice 给设备端下发指令
func (s *signalV2) SendCmdToDevice(userId int64, in *SignalPo.CommandReq) error {
	var (
		err error
	)

	//  查询sn码
	sn, err := Ebike.GetSn(in.EbikeId)
	if err != nil {
		return fmt.Errorf("get sn code failed")
	}

	if in.Cmd == 0 {
		// 发送一键启动命令通知
		event.Emit(&event.Data{
			Type: event.Event_Power_On_Cmd,
			Data: &event.BikePowerOnCmd{
				UserId: userId,
				BikeId: in.EbikeId,
			},
		})

		defer func() {
			if err != nil {
				// 添加一键启动
				trace_log.DebugLogger(sn, "SendCmdToDevice delete power key")
				key := fmt.Sprintf("%v", in.EbikeId)
				if _err := redis.KV.DelValue(context.Background(), DevicePowerOnStatusKey, key); _err != nil {
					log.Errorf("deviceStatus DelValue err:%v", _err)
				}
			}
		}()
	}

	cmd := &dto.CommandRequest{
		Sn:  sn,
		Cmd: dto.CmdType(in.Cmd),
	}

	// 执行指令
	err = RpcCall(sn, func(addr string, err error) error {
		if err != nil {
			return err
		}

		_, err = grpc.Cmd.Command(context.Background(), cmd, func(options *client.CallOptions) {
			options.Address = []string{addr}
		})
		return err
	})

	if err != nil {
		fmt.Println("SendCmdToDevice error  bikeId:", in.EbikeId, "data:", utils.ToJsonString(cmd))
		return errors.DeviceCommandFailed.Error()
	}

	if in.Cmd == 1 {
		// 发送一键关闭命令通知
		event.Emit(&event.Data{
			Type: event.Event_Power_Off_Cmd,
			Data: &event.BikePowerOffCmd{
				UserId: userId,
				BikeId: in.EbikeId,
			},
		})
	}

	// 发射事件
	goroutine_pool.GetPool().Push(nil, func(data interface{}) error {
		event.Emit(&event.Data{
			Type: event.Event_Bike_Cmd,
			Data: &event.BikeCmd{
				EbikeId: in.EbikeId,
				CmdType: "cmd",
				Body:    cmd,
			},
		})
		return nil
	})

	return nil
}

// SendToggleToDevice 给设备端下发指令
func (s *signalV2) SendToggleToDevice(in *SignalPo.ToggleReq) error {
	var (
		err error
	)

	//  查询sn码
	sn, err := Ebike.GetSn(in.EbikeId)
	if err != nil {
		return fmt.Errorf("get sn code failed")
	}

	// 执行指令
	in.Sn = sn
	value := (*dto.SwitchRequest)(unsafe.Pointer(uintptr(unsafe.Pointer(in)) + uintptr(8)))
	err = RpcCall(sn, func(addr string, err error) error {
		if err != nil {
			return err
		}

		_, err = grpc.Cmd.Switch(context.Background(), value, func(options *client.CallOptions) {
			options.Address = []string{addr}
		})
		return err
	})

	if err != nil {
		fmt.Println("SendToggleToDevice  error bikeId:", in.EbikeId, " data:", utils.ToJsonString(value))
		return errors.DeviceCommandFailed.Error()
	}

	event.Emit(&event.Data{
		Type: event.Event_Bike_Cmd,
		Data: &event.BikeCmd{
			EbikeId: in.EbikeId,
			CmdType: "toggle",
			Body:    value,
		},
	})
	return nil
}

// SendMsgToDevice 发送信息给设备端
func (s *signalV2) SendMsgToDevice(in *SignalPo.MessageReq) error {
	var (
		err error
	)

	//  查询sn码
	sn, err := Ebike.GetSn(in.EbikeId)
	if err != nil {
		return fmt.Errorf("get sn code failed")
	}

	// 执行指令
	in.Sn = sn
	value := (*dto.SettingRequest)(unsafe.Pointer(uintptr(unsafe.Pointer(in)) + uintptr(8)))

	fmt.Println("SendMsgToDevice bikeId:", in.EbikeId, " data:", utils.ToJsonString(value))
	err = RpcCall(sn, func(addr string, err error) error {
		if err != nil {
			return err
		}

		_, err = grpc.Cmd.Setting(context.Background(), value, func(options *client.CallOptions) {
			options.Address = []string{addr}
		})
		return err
	})

	if err != nil {
		fmt.Println("SendMsgToDevice error bikeId:", in.EbikeId, " data:", utils.ToJsonString(value))
		return errors.DeviceCommandFailed.Error()
	}

	event.Emit(&event.Data{
		Type: event.Event_Bike_Cmd,
		Data: &event.BikeCmd{
			EbikeId: in.EbikeId,
			CmdType: "set",
			Body:    value,
		},
	})
	return nil
}

// BinaryReq 给设备端下发二进制指令
func (s *signalV2) BinaryReq(sn string, cmd string) (err error) {
	// 给车辆下发超出电子围栏通知
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	bytes, _ := hex.DecodeString(cmd)
	err = RpcCall(sn, func(addr string, err error) error {
		if err != nil {
			return err
		}

		if _, err = grpc.Cmd.BinaryReq(ctx, &dto.BinaryRequest{
			Sn:  sn,
			Hex: bytes,
		}, func(options *client.CallOptions) {
			options.Address = []string{addr}
		}); err != nil {
			return err
		}
		return err
	})

	return err
}

// GetMsgFromDevice 查询设备信息
func (s *signalV2) GetMsgFromDevice(sn string, in *SignalPo.GetMessageReq) (interface{}, error) {
	defer utils.ExceptionCatch()
	var (
		err error
	)

	//  查询sn码
	if sn == "" {
		sn, err = Ebike.GetSn(in.EbikeId)
		if err != nil {
			return nil, fmt.Errorf("get sn code failed")
		}
	}

	switch in.Cmd {
	case "Battery":
		var ret *dto.BatteryReply
		err = RpcCall(sn, func(addr string, err error) error {
			if err != nil {
				return err
			}

			ret, err = grpc.Cmd.Battery(context.Background(), &dto.BatteryRequest{Sn: sn}, func(options *client.CallOptions) {
				options.Address = []string{addr}
			})
			return err
		})

		if err != nil {
			trace_log.DebugLogger(sn, "GetMsgFromDevice Battery error bikeId:", in.EbikeId, " data:", sn, " err:", err)
			// 查询设备影子
			shadowRsp, _ := grpc.SCmd.GetShadowAttributes(context.Background(), &dto.GetShadowAttributesRequest{
				DeviceName: sn,
			})

			if shadowRsp == nil {
				return nil, errors.DeviceCommandFailed.Error()
			}

			var attribute model.ShadowAttribute
			if shadowRsp.Attribute != "" {
				err = json.Unmarshal([]byte(shadowRsp.Attribute), &attribute)
				if err != nil {
					return nil, err
				}
			}

			return &dto.BatteryReply{
				Soc: int32(utils.StrToInt64(attribute.Soc) / 10),
			}, nil
		}

		if ret == nil {
			ret = &dto.BatteryReply{}
		} else {
			syncMap := map[string]interface{}{
				"status":      int32(ret.Status),
				"loop":        ret.Loop,
				"remain_time": ret.RemainTime,
				"soc":         ret.Soc,
				"soe":         ret.Soe,
				"soh":         ret.Soh,
				"temperature": ret.Temperature,
				"voltage":     ret.Voltage,
			}

			// 同步电池属性
			goroutine_pool.GetPoolV2("battery").Push(nil, func(d interface{}) error {
				defer utils.ExceptionCatch()
				if err != nil || ret == nil {
					return nil
				}
				// 同步设备信息到设备影子
				event.Emit(&event.Data{
					Type: event.Event_Mq_SyncDevice,
					Data: &event.MqSyncDevice{
						Sn:         sn,
						UpdateType: dto.EnUpdateType_ATTRIBUTE,
						MapValue:   syncMap,
					},
				})
				return nil
			})
		}

		ret.Soc = ret.Soc / 10
		data, _ := utils.TransProtoToJson(ret)

		trace_log.DebugLogger(sn, "GetMsgFromDevice Battery data:", utils.ToJsonString(ret))
		return data, nil
	case "Location":
		var ret *dto.Location
		err = RpcCall(sn, func(addr string, err error) error {
			if err != nil {
				return err
			}

			ret, err = grpc.Cmd.Location(context.Background(), &dto.LocationRequest{Sn: sn}, func(options *client.CallOptions) {
				options.Address = []string{addr}
			})
			return err
		})

		if err != nil {
			fmt.Println("GetMsgFromDevice Location error bikeId:", in.EbikeId, " data:", sn, " err:", err)
			return nil, err
		}

		var location *po.Point
		if ret == nil {
			location = &po.Point{}
		}

		location = &po.Point{
			X: float64(ret.Lng) / 1000000.0,
			Y: float64(ret.Lat) / 1000000.0,
		}

		goroutine_pool.GetPool().Push(nil, func(data interface{}) error {
			_data, _ := json.Marshal(location)
			event.Emit(&event.Data{
				Type: event.Event_Mq_SyncDevice,
				Data: &event.MqSyncDevice{
					Sn:         sn,
					UpdateType: dto.EnUpdateType_ATTRIBUTE,
					MapValue: map[string]interface{}{
						"location": string(_data),
					},
				},
			})
			return nil
		})
		return location, nil
	case "Controller":
		ret, err := s.getControllerInfo(context.Background(), sn)
		if err != nil {
			return nil, err
		}

		data, _ := utils.TransProtoToJson(ret)
		return data, nil

	case "FirmwareVersion":
		var ret *dto.FirmwareVersionReply
		err = RpcCall(sn, func(addr string, err error) error {
			if err != nil {
				return err
			}

			ret, err = grpc.Cmd.FirmwareVersion(context.Background(), &dto.FirmwareVersionRequest{Sn: sn}, func(options *client.CallOptions) {
				options.Address = []string{addr}
			})
			return err
		})
		if err != nil {
			return nil, err
		}

		return ret, nil
	}

	return nil, errors.New(fmt.Sprintf("not exist rpc handler name:%v", in.Cmd))
}

// getControllerInfo 查询设备信息
func (s *signalV2) getControllerInfo(ctx context.Context, sn string) (*dto.ControllerReply, error) {
	var (
		ret *dto.ControllerReply
		err error
	)

	err = RpcCall(sn, func(addr string, err error) error {
		if err != nil {
			return err
		}

		ret, err = grpc.Cmd.Controller(ctx, &dto.ControllerRequest{Sn: sn}, func(options *client.CallOptions) {
			options.Address = []string{addr}
		})
		if err != nil {
			fmt.Println("GetMsgFromDevice Controller error data:", sn, " err:", err)
			return errors.DeviceCommandFailed.Error()
		}
		return err
	})

	if err != nil {
		return nil, err
	}

	if ret == nil {
		ret = &dto.ControllerReply{}
	} else {
		if ret.CcuMac != "" {
			// 同步电池属性
			goroutine_pool.GetPool().Push(nil, func(d interface{}) error {
				defer utils.ExceptionCatch()
				if err != nil || ret == nil {
					return nil
				}

				// 同步设备信息到设备影子
				event.Emit(&event.Data{
					Type: event.Event_Mq_SyncDevice,
					Data: &event.MqSyncDevice{
						Sn:         sn,
						UpdateType: dto.EnUpdateType_ATTRIBUTE,
						MapValue: map[string]interface{}{
							"ccu_mac":   ret.CcuMac,
							"sim_imei":  ret.SimImei,
							"sim_imsi":  ret.SimImsi,
							"sim_iccid": ret.SimIccid,
						},
					},
				})

				// 发射事件
				event.Emit(&event.Data{
					Type: event.Event_Control_Report,
					Data: &event.ControlReport{
						SnCode: sn,
						Data:   ret,
					},
				})
				return nil
			})
		}
	}
	return ret, nil
}

func (s *signalV2) reactiveCmd(c *gin.Context) {
	var (
		err error
		in  = &dto.ReactiveRequest{}
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if err = Signal.ReactiveCmd(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, nil)
}
